#pragma once 
#include <stdio.h>
#include <stdlib.h>
#include <sprng.h>
#include <fstream>

#include "clUtil/clUtil.h"
#include "../qmc.h"
#include "../WalkerGroup.h"
#include "../kernels.h"
#include "../GridException.h"
#include "../params.h"

#define GRID_SPACING 7.0

using namespace std;

class WalkerGroupOpenCL : public WalkerGroup
{
  public:
    void compute(cl_event* event);
    void writeToDisk(const char* filename);
    void getX(float* output);
    void getY(float* output);
    void getZ(float* output);
    WalkerGroupOpenCL(WalkerGroup* initial, 
                      unsigned int numWalkers,
                      unsigned int walkersStart);
    WalkerGroupOpenCL(unsigned int xDim,
                      unsigned int yDim,
                      unsigned int zDim);
    WalkerGroupOpenCL(const char* filename);
    ~WalkerGroupOpenCL();
    double getEnergy();
  protected:
    static bool runtimeInitialized;
    void allocate(unsigned int numParticles,
                  unsigned int numWalkers);
    void initialize();
    void initSprng();
    void perturb();
    void computePartials();
    void reducePartials(bool initial = false);
    void accept();
    WalkerGroupOpenCL(){}
    int** mRandomGen;
    void debug();
    unsigned int mWalkerStart;
    cl_mem mCurBuffer;
    cl_mem mX;
    cl_mem mY;
    cl_mem mZ;
#ifdef GPU_SPRNG
    cl_mem mGenerators;
#else
    int** mGenerators;
    float* mRandomBuffer;
    cl_mem mRandomBufferGPU;
#endif
    cl_mem mWaveFunction;
    cl_mem mPartialWaveFunction;
    cl_mem mPotential;
    cl_mem mPartialPotential;
    cl_mem mKinetic;
    cl_mem mPartialKinetic;
    cl_mem mQForceX;
    cl_mem mQForceY;
    cl_mem mQForceZ;
    cl_mem mTotalEnergy;
};
